#include <stdio.h>
#include <ctype.h>
#include "CBCalcKeyPad.h"
#include "Util.h"

#define PointInRect(px, py, x, y, w, h)     \
    ((px) >= (x) && (py) >= (y) && (px) < ((x) + (w)) && (py) < ((y) + (h)))

CBCalcKeyPad::CBCalcKeyPad()
{
    CMzString skinNormalPath = GetResourcePath(TEXT("normal.png"));
    CMzString skinPressedPath = GetResourcePath(TEXT("pressed.png"));
    CMzString keysPath = GetResourcePath(TEXT("keys.txt"));

    m_skinNormal.LoadImage(skinNormalPath, true, false, false);
    m_skinPressed.LoadImage(skinPressedPath, true, false, false);

    LoadKeys(keysPath);
}

void
CBCalcKeyPad::LoadKeys(const CMzString& keysPath)
{
    FILE* f = _wfopen(keysPath.C_Str(), L"r");
    if (NULL == f)
        return;

    char line[128], *p;
    int x, y, w, h;
    while (NULL != fgets(line, sizeof(line)/sizeof(char), f)) {
        p = line;
        while (isspace(*p))
            ++p;
        if (*p == '#')
            continue;

        if (4 != sscanf(p, "%d%d%d%d", &x, &y, &w, &h))
            continue;

        p = strchr(p, ':');
        if (NULL == p)
            continue;
        ++p;

        int len = strlen(p);
        if (len == 0)
            continue;
        if (p[len - 1] == '\n') {
            if (len == 1)
                continue;
            p[len - 1] = 0;
            --len;
        }
        if (p[len - 1] == '\r') {
            if (len == 1)
                continue;
            p[len - 1] = 0;
        }

        m_buttons.push_back(CBCalcButton(x, y, w, h, p));
    }

    fclose(f);

    m_currentButton = m_buttons.size();
}

int
CBCalcKeyPad::FindButton(int x, int y)
{
    int n = m_buttons.size();
    int i;

    for (i = 0; i < n; ++i) {
        CBCalcButton& btn = m_buttons[i];
        if (PointInRect(x, y, btn.x, btn.y, btn.w, btn.h))
            break;
    }

    return i;
}

void
CBCalcKeyPad::OnInit(CBCalcUiEdit* uiedit)
{
    m_uiedit = uiedit;

    const TCHAR* format = TEXT("%s: expected %d x %d, actual %d x %d");
    const TCHAR* title = TEXT("Wrong skin");
    int skin;

    int w = GetWidth(), h = GetHeight();
    int iw, ih;

    skin = 0;
    iw = m_skinNormal.GetImageWidth();
    ih = m_skinNormal.GetImageHeight();

    if (w != iw || h != ih)
        goto L_badskin;

    skin = 1;
    iw = m_skinPressed.GetImageWidth();
    ih = m_skinPressed.GetImageHeight();

    if (w != iw || h != ih)
        goto L_badskin;

    if (m_buttons.size() == 0)
        MzMessageBoxEx(GetParentWnd(), TEXT("keys.txt: no key defined!"), TEXT("Wrong skin"));

    return;

L_badskin:
    TCHAR content[128];
    _sntprintf(content, 128, format,
               skin == 0 ? TEXT("normal.png") : TEXT("pressed.png"),
               w, h, iw, ih);
    content[127] = 0;
    MzMessageBoxEx(GetParentWnd(), content, title);

    m_skinNormal.UnloadImage();
    m_skinPressed.UnloadImage();
}

CBCalcKeyPad::~CBCalcKeyPad()
{
    m_skinNormal.UnloadImage();
    m_skinPressed.UnloadImage();
}

void
CBCalcKeyPad::PaintWin(HDC hdc, RECT*  prcWin, RECT*  prcUpdate)
{
    if (! m_skinNormal.IsLoadedToDC() || ! m_skinPressed.IsLoadedToDC()) {
        UiWin::PaintWin(hdc, prcWin, prcUpdate);
        return;
    }

    ::BitBlt(hdc,
             prcUpdate->left, prcUpdate->top,
             RECT_WIDTH(*prcUpdate), RECT_HEIGHT(*prcUpdate),
             m_skinNormal.GetDC(),
             prcUpdate->left - GetPosX(), prcUpdate->top - GetPosY(), SRCCOPY);
}

BOOL
CBCalcKeyPad::BitBlt(int x, int y, int w, int h, ImagingHelper& img)
{
    HWND hWnd = GetParentWnd();
    HDC hDC = GetDC(hWnd);
    BOOL bRet = ::BitBlt(hDC, GetPosX() + x, GetPosY() + y, w, h, img.GetDC(), x, y, SRCCOPY);
    ReleaseDC(hWnd, hDC);

    return bRet;
}

int
CBCalcKeyPad::OnLButtonDown(UINT fwKeys, int xPos, int yPos)
{
    int ret = UiWin::OnLButtonDown(fwKeys, xPos, yPos);

    int i = FindButton(xPos, yPos);

    if (i < m_buttons.size() && m_skinPressed.IsLoadedToDC()) {
        BitBlt(m_buttons[i].x, m_buttons[i].y, m_buttons[i].w, m_buttons[i].h, m_skinPressed);
        m_currentButton = i;
    }

    return ret;
}

int
CBCalcKeyPad::OnLButtonUp(UINT fwKeys, int xPos, int yPos)
{
    int ret = UiWin::OnLButtonUp(fwKeys, xPos, yPos);
    int n = m_buttons.size();

    if (m_currentButton < n) {
        CBCalcButton& btn = m_buttons[m_currentButton];

        if (m_skinNormal.IsLoadedToDC())
            BitBlt(btn.x, btn.y, btn.w, btn.h, m_skinNormal);

        if (PointInRect(xPos, yPos, btn.x, btn.y, btn.w, btn.h)) {
            std::string& s = btn.str;
            m_uiedit->appendString(s.c_str(), s.length());
        }

        m_currentButton = n;
    }

    return ret;
}

